//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include "setup.h"

#include <asm.h>

#if defined(_GNUC)

EXTERN DECL_CODEINIT void CDECL KernelSetupEntry()
{
DECL_ASMENTRY(KrnSetup)
    ASM("str    lr, OldLR;"
        "str    sp, OldSP;"
        "str    fp, OldFP;"
        "ldr    lr, RetAdr;"
        "b      "C_PREFIX"__KernelSetup;"
        "OldLR:     .long 0;"
        "OldSP:     .long 0;"
        "OldFP:     .long 0;"
        "RetAdr:    .long "C_PREFIX"__KernelSetupExit;"
        C_PREFIX"__KernelSetupExit:");

    ASM("sub    %1, %1, %0;"

        "adr    r0, OldLR;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    lr, r1;"

        "adr    r0, OldSP;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    sp, r1;"

        "adr    r0, OldFP;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    fp, r1;"

        "mov    pc, lr;"
        : :"r"(bsp.paKernelPhysBase), "r"(KERNEL_BASE) :"r0", "r1");
}

#ifndef _neptune

EXTERN DECL_CODEINIT void CDECL SetupMMUEntry()
{
DECL_ASMENTRY(SetupMMU)

    ASM("str    lr, OldLR2;"
        "str    sp, OldSP2;"
        "str    fp, OldFP2;"
        "ldr    lr, RetAdr2;"
        "b      "C_PREFIX"__SetupMMU;"
        "OldLR2:    .long 0;"
        "OldSP2:    .long 0;"
        "OldFP2:    .long 0;"
        "RetAdr2:   .long "C_PREFIX"__SetupMMUExit;"
        C_PREFIX"__SetupMMUExit:");

    ASM("sub    %1, %1, %0;"

        "adr    r0, OldLR2;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    lr, r1;"

        "adr    r0, OldSP2;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    sp, r1;"

        "adr    r0, OldFP2;"
        "ldr    r1, [r0];"
        "add    r1, r1, %1;"
        "mov    fp, r1;"

        "mov    pc, lr;"
        : :"r"(bsp.paKernelPhysBase), "r"(KERNEL_BASE) :"r0", "r1");
}

EXTERN_C DECL_CODEINIT void CDECL __SetupMMU(
    physaddr_t paKernelPhysBase, physaddr_t paKernelImagePhysBase,
    physaddr_t paMemoryMappedIoBase, physaddr_t paMemoryMappedIoLimit)
{
    SetupPaging(
        paKernelPhysBase, paKernelImagePhysBase,
        paMemoryMappedIoBase, paMemoryMappedIoLimit);

    ASM("ldr    pc, NextAddr;"
        "NextAddr:  .long   _Next;"
        "_Next:");

    ASM("sub    %1, %1, %0;"
        "add    sp, sp, %1;"
        "add    fp, fp, %1;"
        : :"r"(bsp.paKernelPhysBase), "r"(KERNEL_BASE));

    AdjustPaging(paKernelPhysBase);
}

#else // _neptune

#include <nu_api.h>

EXTERN_C DECL_CODEINIT void CDECL SetupMMU(
    physaddr_t paKernelPhysBase, physaddr_t paKernelImagePhysBase,
    physaddr_t paMemoryMappedIoBase, physaddr_t paMemoryMappedIoLimit)
{
    virtaddr_t c2 = VIRTADDR(GetPageDirAddress());

    uint_t uSectionBase, uPrevSectionLimit = 0;
    nuMmu_SectInfo *pSectInfo;
    Uint8 sectInfoSize;

    nu_getMmuSectInfo(&pSectInfo, &sectInfoSize);

    assert(0 != sectInfoSize);

    for (uint_t i = 0; i < sectInfoSize; i++) {
        uSectionBase = PHYSADDR_TO_PAGETABNO(pSectInfo[i].pa);

        if (uPrevSectionLimit < uSectionBase) {
            memset(&g_initPageDir[uPrevSectionLimit], 0,
                (uSectionBase - uPrevSectionLimit) * sizeof(PageDirEntry));
        }

        memcpy(&g_initPageDir[uSectionBase], &((uint_t *)c2)[uSectionBase],
            pSectInfo[i].numEntries * sizeof(PageDirEntry));

        uPrevSectionLimit = uSectionBase + pSectInfo[i].numEntries;
    }

    if (uPrevSectionLimit < c_uNumberOfPageDirEntries) {
        memset(&g_initPageDir[uPrevSectionLimit], 0,
            (c_uNumberOfPageDirEntries - uPrevSectionLimit)
                * sizeof(PageDirEntry));
    }

    FlushCache();
    SetPageDirAddress(PHYSADDR(g_initPageDir));
    FlushAllTlbs();
}

#endif // _neptune

#endif // defined(_GNUC)
